package Boke2;

import java.util.concurrent.PriorityBlockingQueue;

//描述定时器执行的一个具体任务
class RunTask implements Comparable<RunTask>{
    private Runnable commond;
    private long after;
    //每个任务的构造方法
    public RunTask(Runnable commond,long after){
        this.commond=commond;
        long now=System.currentTimeMillis();
        //获取精准的任务执行时间戳
        this.after=now+after;
    }
    //执行任务
    public void run(){
        this.commond.run();
    }
    public long getAfter(){
        return after;
    }
    //重写任务之前的比较规则

    @Override
    public int compareTo(RunTask o) {
        return (int)(this.after-o.after);
    }
}


public class MyTimer {
    //用于防止任务"空等",进行阻塞等待的锁对象
    private Object locker=new Object();
    //该优先级阻塞队列用于存放定时器中的所有任务
    private PriorityBlockingQueue<RunTask> queue=new PriorityBlockingQueue<>();
    public void schedule(Runnable commond,long after){
        //构造一个具体定时器执行任务
        RunTask task=new RunTask(commond,after);
        //此处加锁是为了防止在取出队列元素和任务阻塞等待过程中提前notify
        //此时就不能及时唤醒阻塞等待中的任务(原因是此时还没有执行wait())
        synchronized (locker){
            //添加到按时间顺序执行的优先级阻塞队列中
            queue.put(task);
            //为了防止在某个任务等待过程中有新任务插入，所以还要唤醒阻塞等待，判断一下
            //新插入的任务是否比前一个正在阻塞等待任务还要先执行
            locker.notify();
        }

    }

    //让定时器创建好之后就一直扫描当前时间是否有任务要执行了
    //也就是用一个线程来并发实现
    public MyTimer(){
        Thread t1=new Thread(()->{
            while(true){
                try {
                    long now=System.currentTimeMillis();
                    synchronized (locker){
                        while(queue.isEmpty()){
                            //当队列没有元素是要先阻塞等待，防止死锁
                            locker.wait();
                        }
                        RunTask task=queue.take();
                        if(task.getAfter()>now){
                            //优先级阻塞队列中第一个最先执行的任务还没到时间
                            //将该任务放回队列并先阻塞等待直到时间到了，防止发生"空等"
                            queue.put(task);
                            locker.wait(task.getAfter()-now);
                        }else{
                            //如果时间到了就立即执行
                            task.run();
                        }
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
    }

    public static void main(String[] args) {
        MyTimer timer1=new MyTimer();
        timer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("111111");
            }
        },1000);

        timer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("2222222");
            }
        },2000);

        timer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("3333333");
            }
        },3000);
    }
}
